#ifndef CHORD_H
#define CHORD_H

#include "ssocket.h"
#include "sem.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <arpa/inet.h>
#include <math.h>
#include <pthread.h>


//maximo de nos da rede
#define PORT 5780
#define CHORDVERSION 1
#define CHORDHEADERSIZE 1
#define MAX_NODE 1024
#define MAX_NEXTS (log(MAX_NODE)/log(2))
#define COP_FIND 1
#define COP_FINDRESPONSE 2
#define COP_UPDATEKEY 3
#define COP_DIVISIONKEY 4
#define COP_DIVISIONKEYRESPONSE 5
#define COP_DISCONNECT 6
#define COP_ASKTO 7
#define IP_MAX_SIZE 15
#define SEM_KEYFIND 0x2000
#define SEM_KEYDIV 0x2001

struct CHnext {
	int ip;
	int port;
};

typedef CHnext CHnext;

/**
	* class Chord
	* Classe da rede Chord, implementa todas os requisitos de uma rede P2P Chord.
	*/

class Chord
{

private:
	// Armazena uma lista circular de vizinhos
	CHnext** nexts;
	// Máquina responsável pelo nó anterior
	CHnext* prior;
	// Quantidade de chaves que o nó é responsável além da que ele tem obrigação
	int numberOfKeys;
	// Armazena o socket de servidor para a rede Chord
	tcpSocketServidor sockServer;
	// Chave que o nó é responsável
	int key;
	int connected;
	//ip da máquina local
	int myIP;
	
	short myPort;
	
	int numero;//nome do processo
	
	int terminar;//se tiver valor > 0, informa que o modo listen deve ser desativado
	
	//utilizado para transferir a informação do nó entre os métodos (busca de no)
	CHnext* tmpNode;
	int tmpNKeys;//utilizado para transferir a resposta de divisao de chaves
	
	//Sem* semFind;//semaforo de busca
	//Sem* semDiv;//semaforo de divisao de chaves
	pthread_mutex_t semFind;
	pthread_mutex_t semDiv;
	
	//variaveis internas para controle de perguntas
	int divisionSended;
	
	
	//semaforo para busca
	//int semFind;
	//semaforo para divisão de chaves
	//int semDiv;

	void setPrior ( CHnext* new_var );
	CHnext* getPrior ( );
	
	/**
	 * Constroi/atualiza a tabela de vizinhos
	 */
	void makeNexts();
	
	/**
	 * Verifica se a chave informada está na área de abrangencia de um certo nó
	 * @param nodeKey chave do nó responsável
	 * @param numberOfKey número de chaves que o nó é responsável
	 * @param keyAnswer chave que será testada
	 *
	 */
	bool inRange(int nodeKey,int numberOfKey,int keyAnswer);
	
	/**
	 * Retorna o ponteiro para o vizinho mais próximo e anterior a chave informada
	 */
	CHnext* nextOfKey(int key);
		
	/**
	 * Set the value of numberOfKeys
	 * Quantidade de chaves que o nó é responsável além da que ele tem obrigação
	 * @param new_var the new value of numberOfKeys
	 */
	void setNumberOfKeys (int new_var );

	/**
	 * Get the value of numberOfKeys
	 * Quantidade de chaves que o nó é responsável além da que ele tem obrigação
	 * @return the value of numberOfKeys
	 */
	int getNumberOfKeys ( );

	/**
	 * Implementa a operação 2: Resposta a busca
	 * @param	node nó que deverá receber a resposta
	 * @param nodeTarget nó que é responsável pela chave
	 */
	void findResponse (CHnext* node,CHnext* nodeTarget);


	/**
	 * Implementa a operação 3: Envia uma mensagem para um nó informando que se ele
	 * possui alguma chave que aponta para as chaves informadas, que deve atualiza-la
	 * em sua tabela.
	 * @param	node nó que deverá ter seus nós atualizados
	 */
	void sendUpdateKeys (CHnext* node);


	/**
	 * Implementa a operação 4: Divisão de chaves
	 * @return int quantidade de nós que será responsável
	 * @param	node nó que receberá a mensagem informada
	 */
	int divisionKeys (CHnext* node);


	/**
	 * Implementa a operação 5: Resposta a divisão de chaves
	 * @param	node nó que deverá receber a mensagem
	 * @param numberOfKeys número de chaves que o novo nó é responsável
	 */
	void divisionKeysResponse (CHnext* node, int numberOfKeys);


	/**
	 * Implementa a operação 7: Perguntar sobre a divisão de chaves ao ip informado
	 * @param	node nó que deverá receber a mensagem
	 * @param	nodeTarget nó que deve ser perguntado
	 */
	void askTo (CHnext* node, CHnext* nodeTarget );
	
	/**
	 * chamado sempre que uma solicitação de busca chega no socket
	 */
	void l_findMachine(tcpSocket& sock);
	
	/**
	 * chamado sempre que uma resposta a busca chega no socket
	 */
	void l_findMachineResponse(tcpSocket& sock);
	
	/**
	 * chamado sempre que uma atualização de chaves chega no socket
	 */
	void l_updateKeys(tcpSocket& sock);
	
	/**
	 * chamado sempre que uma solicitação de divisão de chaves chega ao socket
	 */
	void l_divisionKeys(tcpSocket& sock);

	/**
	 * chamado sempre que uma resposta a solicitação de divisão de chaves chega ao socket
	 */
	void l_divisionKeysResponse(tcpSocket& sock);
	
	/**
	 * chamado sempre que uma informação de que está deixando a rede chega ao socket
	 */
	void l_disconnect(tcpSocket& sock);
	
	/**
	 * chamado sempre que uma mensagem de "Pergunte a outro" chega no socket
	 */
	void l_askTo(tcpSocket& sock);
	
	
	
	/**
	 * Envia o buffer para o ip informado (abre conexão, acrescenta o cabeçalho e envia
	 * os dados)
	 * @return int
	 * @param	operacao
	 * @param	buffer
	 * @param	length
	 * @param	node
	 */
	int send (int operacao,void* buffer, int length, CHnext* node );

	
	/**
	 * Monta o quadro de busca, e envia para o ip mais proximo desta chave
	 * @param key chave que procura
	 * @param nodeFind nó que está procurando
	 * @param node para que nó a mensagem será enviada
	 */
	void sendFindMachine(int key, CHnext* nodeFind, CHnext* node );
		
	
public:

	/**
	 * método onde será implementado a thread principal
	 */
	void listen();
	
	/**
	 * método de conversão de ip (char) para ip (int) 
	 * método depreciado
	 */
	int ipCharToInt(char* ipAddress);

	/**
	 * converte um número ip (INT) para um char
	 */
	char* ipIntToChar(int ipAddress);

	/**
	 * cria a classe, reserva a memória necessária para todos os ponteiros
	 */
	Chord(int key,short port, int numero);
	
	/**
	 * Libera a memória utilizada pela rede
	 */
	~Chord();

	/**
	 * Converte um endereço IP em uma chave
	 * @return unsigned int
	 * @param	key Chave que deseja procurar na rede
	 */
	CHnext* findMachine (int key );


	/**
	 * Mapeia um nome de recurso em uma chave
	 * @return unsigned int
	 * @param	ipAddress Endereço IP
	 */
	int machineToKey (char* ipAddress );


	/**
	 * Resouve um nome de recurso em uma chave
	 * rede
	 * @return unsigned int
	 * @param	resource Nome do recurso que deverá ser mapeado
	 */
	int resourceToKey (char* resource );


	/**
	 * Implementa a operação de connectar a rede
	 * @return bool
	 * @param nodeAddress Ip de um dos nós da rede, para que a introdução à rede seja
	 * feita por ele
	 */
	void connectToNetwork (char* nodeAddress, short port);
	
	/**
	 * Implementa a operação de connectar a rede
	 * @return bool
	 * @param nodeAddress Ip de um dos nós da rede, para que a introdução à rede seja
	 * feita por ele
	 */
	void createNetwork (char* nodeAddress, short port);


	/**
	 * Implementa a operação de código 6: Deixando a rede
	 */
	void disconnect ( );

	/**
	 * Set the value of m_sockServer
	 * Armazena o socket de servidor para a rede Chord
	 * @param new_var the new value of m_sockServer
	 */
	void setSockServer ( tcpSocketServidor new_var );

	/**
	 * Get the value of m_sockServer
	 * Armazena o socket de servidor para a rede Chord
	 * @return the value of m_sockServer
	 */
	tcpSocketServidor getSockServer ( );


	/**
	 * Set the value of m_key
	 * Chave que o nó é responsável
	 * @param new_var the new value of m_key
	 */
	void setKey (int new_var );

	/**
	 * Get the value of m_key
	 * Chave que o nó é responsável
	 * @return the value of m_key
	 */
	int getKey ( );

};
#endif // CHORD_H
